#include <stdlib.h>
#include <typeinfo>
#include "Arbre.h"
#include "Symbole.h"
#include "SymboleValue.h"




////////////////////////////////////////////////////////////////////////////////
// NoeudSeqInst
////////////////////////////////////////////////////////////////////////////////

NoeudSeqInst::NoeudSeqInst() : tabInst() {
}

////////////////////////////////////////////////////////////////////////////////
int NoeudSeqInst::getValeur() {
    int valeur=0;
    for (unsigned int i=0; i<tabInst.size(); i++)
        valeur = tabInst[i]->getValeur();  // on evalue chaque instruction de la séquence
    return valeur; // par convention, resultat = valeur de la derniere instruction
}

////////////////////////////////////////////////////////////////////////////////
// void NoeudSeqInst::afficher(unsigned short indentation) {
//   Noeud::afficher(indentation);
//   cout << "Noeud - Sequence de " << tabInst.size() << " instruction(s)" << endl;
//   for (unsigned int i=0; i<tabInst.size(); i++)
//     tabInst[i]->afficher(indentation+1); // on affiche les fils en augmentant l'indentation
// }

////////////////////////////////////////////////////////////////////////////////
int NoeudSeqInst::getSize() {
    return tabInst.size();
}

////////////////////////////////////////////////////////////////////////////////
void NoeudSeqInst::ajouteInstruction(Noeud* instruction) {
    tabInst.push_back(instruction);
}

void NoeudSeqInst::Accept (NoeudVisitor& visitor) {
    if (typeid(visitor) == typeid(AfficherVisitor))
        visitor.IncresIndentation();
    visitor.VisitNoeudSeqInst(this);
    for (unsigned int i=0; i<tabInst.size(); i++)
        tabInst[i]->Accept(visitor); // on affiche les fils en augmentant l'indentation
    if (typeid(visitor) == typeid(AfficherVisitor))
        visitor.DecresIndentation();
}





////////////////////////////////////////////////////////////////////////////////
// NoeudSi
////////////////////////////////////////////////////////////////////////////////

NoeudSi::NoeudSi(Noeud* expbool, Noeud* seqInst, Noeud* siSuivant) {
    this->expBool=expbool;
    this->seqInst=seqInst;
    this->siSuivant=siSuivant;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudSi::getValeur() {
    int valeur = 1; // on évalue l'expression
    if (expBool->getValeur() == 1) seqInst->getValeur();
    if (siSuivant != NULL) siSuivant->getValeur();
//  ((SymboleValue*)variable)->setValeur(valeur); // on affecte la variable
    return valeur; // par convention, une affectation a pour valeur la valeur affectée
}

////////////////////////////////////////////////////////////////////////////////
// void NoeudSi::afficher(unsigned short indentation) {
//   Noeud::afficher(indentation);
//   cout << "Noeud - Si" << endl;
//   expBool->afficher(indentation+1);   // on affiche variable et expression
//   seqInst->afficher(indentation+1); // en augmentant l'indentation
//   if (siSuivant != NULL)
// 	  siSuivant->afficher(indentation+1); // en augmentant l'indentation
// }

void NoeudSi::Accept (NoeudVisitor& visitor) {
    if (typeid(visitor) == typeid(AfficherVisitor))
        visitor.IncresIndentation();
    visitor.VisitNoeudSi(this);
    expBool->Accept(visitor);
    seqInst->Accept(visitor);
    if (siSuivant != NULL)
        siSuivant->Accept(visitor);
    if (typeid(visitor) == typeid(AfficherVisitor))
        visitor.DecresIndentation();
}



////////////////////////////////////////////////////////////////////////////////
// NoeudPour
////////////////////////////////////////////////////////////////////////////////

NoeudPour::NoeudPour(Noeud* aff, Noeud* affDroite, Noeud* expbool, Noeud* seqInst) {
    this->expBool=expbool;
    this->seqInst=seqInst;
    this->aff=aff;
    this->affDroite=affDroite;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudPour::getValeur() {
    int valeur = 1; // on évalue l'expression
//  ((SymboleValue*)variable)->setValeur(valeur); // on affecte la variable
    for (aff->getValeur();expBool->getValeur(); affDroite->getValeur())
        seqInst->getValeur(); // on affiche les fils en augmentant l'indentation
    return valeur; // par convention, une affectation a pour valeur la valeur affectée
}

////////////////////////////////////////////////////////////////////////////////
// void NoeudPour::afficher(unsigned short indentation) {
//   Noeud::afficher(indentation);
//   cout << "Noeud - Pour" << endl;
//   aff->afficher(indentation+1);   // on affiche variable et expression
//   expBool->afficher(indentation+1);   // on affiche variable et expression
//   affDroite->afficher(indentation+1);   // on affiche variable et expression
//   seqInst->afficher(indentation+1); // en augmentant l'indentation
// }

void NoeudPour::Accept (NoeudVisitor& visitor) {
    if (typeid(visitor) == typeid(AfficherVisitor))
        visitor.IncresIndentation();
    visitor.VisitNoeudPour(this);
    aff->Accept(visitor);
    expBool->Accept(visitor);
    affDroite->Accept(visitor);
    seqInst->Accept(visitor);
    if (typeid(visitor) == typeid(AfficherVisitor))
        visitor.DecresIndentation();
}



////////////////////////////////////////////////////////////////////////////////
// NoeudTantQue
////////////////////////////////////////////////////////////////////////////////

NoeudTantQue::NoeudTantQue(Noeud* expbool, Noeud* seqInst) {
    this->expBool=expbool;
    this->seqInst=seqInst;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudTantQue::getValeur() {
    int valeur =1; // on évalue l'expression
    while (expBool->getValeur() == 1) {
        seqInst->getValeur();
    }
//  ((SymboleValue*)variable)->setValeur(valeur); // on affecte la variable
    return valeur; // par convention, une affectation a pour valeur la valeur affectée
}

////////////////////////////////////////////////////////////////////////////////
// void NoeudTantQue::afficher(unsigned short indentation) {
//   Noeud::afficher(indentation);
//   cout << "Noeud - TantQue" << endl;
//   expBool->afficher(indentation+1);   // on affiche variable et expression
//   seqInst->afficher(indentation+1); // en augmentant l'indentation
//
// }

void NoeudTantQue::Accept (NoeudVisitor& visitor) {
    if (typeid(visitor) == typeid(AfficherVisitor))
        visitor.IncresIndentation();
    visitor.VisitNoeudTantQue(this);
    expBool->Accept(visitor);
    seqInst->Accept(visitor);
    if (typeid(visitor) == typeid(AfficherVisitor))
        visitor.DecresIndentation();
}


////////////////////////////////////////////////////////////////////////////////
// NoeudRepeter
////////////////////////////////////////////////////////////////////////////////

NoeudRepeter::NoeudRepeter(Noeud* expbool, Noeud* seqInst) {
    this->expBool=expbool;
    this->seqInst=seqInst;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudRepeter::getValeur() {
    int valeur =1; // on évalue l'expression
    do {
        seqInst->getValeur();
    }
    while (expBool->getValeur() == 1);
//  ((SymboleValue*)variable)->setValeur(valeur); // on affecte la variable
    return valeur; // par convention, une affectation a pour valeur la valeur affectée
}

////////////////////////////////////////////////////////////////////////////////
// void NoeudRepeter::afficher(unsigned short indentation) {
//   Noeud::afficher(indentation);
//   cout << "Noeud - Repeter" << endl;
//   seqInst->afficher(indentation+1); // en augmentant l'indentation
//   expBool->afficher(indentation+1);   // on affiche variable et expression
// }

void NoeudRepeter::Accept (NoeudVisitor& visitor) {
    if (typeid(visitor) == typeid(AfficherVisitor))
        visitor.IncresIndentation();
    visitor.VisitNoeudRepeter(this);
    expBool->Accept(visitor);
    seqInst->Accept(visitor);
    if (typeid(visitor) == typeid(AfficherVisitor))
        visitor.DecresIndentation();
}

////////////////////////////////////////////////////////////////////////////////
// NoeudAffectation
////////////////////////////////////////////////////////////////////////////////

NoeudAffectation::NoeudAffectation(Noeud* variable, Noeud* expression) {
    this->variable=variable;
    this->expression=expression;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudAffectation::getValeur() {
    int valeur = expression->getValeur(); // on évalue l'expression
    ((SymboleValue*)variable)->setValeur(valeur); // on affecte la variable
    return valeur; // par convention, une affectation a pour valeur la valeur affectée
}

////////////////////////////////////////////////////////////////////////////////
// void NoeudAffectation::afficher(unsigned short indentation) {
//   Noeud::afficher(indentation);
//   cout << "Noeud - Affectation" << endl;
//   variable->afficher(indentation+1);   // on affiche variable et expression
//   expression->afficher(indentation+1); // en augmentant l'indentation
// }

void NoeudAffectation::Accept (NoeudVisitor& visitor) {
    if (typeid(visitor) == typeid(AfficherVisitor))
        visitor.IncresIndentation();
    visitor.VisitNoeudAffectation(this);
    this->variable->Accept(visitor);
    this->expression->Accept(visitor);
    if (typeid(visitor) == typeid(AfficherVisitor))
        visitor.DecresIndentation();
}



////////////////////////////////////////////////////////////////////////////////
// NoeudLire
////////////////////////////////////////////////////////////////////////////////

NoeudLire::NoeudLire(Noeud* variable) {
    this->variable=variable;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudLire::getValeur() {
    int valeur;
//  ((SymboleValue*)variable)->setValeur(valeur); // on affecte la variable
// return valeur; // par convention, une affectation a pour valeur la valeur affectée
//  variable->afficher();
    cout << "Nouvelle valeur ?" << endl;
    cin >> valeur;
    ((SymboleValue*)variable)->setValeur(valeur); // on affecte la variable
    return valeur; // par convention, une affectation a pour valeur la valeur affectée
}

////////////////////////////////////////////////////////////////////////////////
// void NoeudLire::afficher(unsigned short indentation) {
//   Noeud::afficher(indentation);
//   cout << "Noeud - Lire" << endl;
//   variable->afficher(indentation+1);   // on affiche variable et expression
// //  expression->afficher(indentation+1); // en augmentant l'indentation
// }

void NoeudLire::Accept (NoeudVisitor& visitor) {
    if (typeid(visitor) == typeid(AfficherVisitor))
        visitor.IncresIndentation();
    visitor.VisitNoeudLire(this);
    variable->Accept(visitor);
    if (typeid(visitor) == typeid(AfficherVisitor))
        visitor.DecresIndentation();
}

////////////////////////////////////////////////////////////////////////////////
// NoeudEcrire
////////////////////////////////////////////////////////////////////////////////

NoeudEcrire::NoeudEcrire(Noeud* expression) {
    this->expression=expression;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudEcrire::getValeur() {
    int valeur = expression->getValeur(); // on évalue l'expression
    return valeur; // par convention, une affectation a pour valeur la valeur affectée
}

////////////////////////////////////////////////////////////////////////////////
// void NoeudEcrire::afficher(unsigned short indentation) {
//   Noeud::afficher(indentation);
//   cout << "Noeud - Ecrire" << endl;
//   expression->afficher(indentation+1); // on affiche l'expression a ecrire en augmentant l'indentation
// }

void NoeudEcrire::Accept (NoeudVisitor& visitor) {
    if (typeid(visitor) == typeid(AfficherVisitor))
        visitor.IncresIndentation();
    visitor.VisitNoeudEcrire(this);
    expression->Accept(visitor);
    if (typeid(visitor) == typeid(AfficherVisitor))
        visitor.DecresIndentation();
}



////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurBinaire
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurBinaire::NoeudOperateurBinaire ( Symbole operateur,
        Noeud* operandeGauche,
        Noeud* operandeDroit) {
    this->operateur=operateur;
    this->operandeGauche=operandeGauche;
    this->operandeDroit=operandeDroit;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudOperateurBinaire::getValeur() {
    int valeur=0;
    int og=operandeGauche->getValeur();
    int od=operandeDroit->getValeur();
    if      (this->operateur=="+") valeur = og+od;
    else if (this->operateur=="-") valeur = og-od;
    else if (this->operateur=="*") valeur = og*od;
    else if (this->operateur=="/") {
        if (od!=0)
            valeur = og/od;
        else {
            cout << "Erreur pendant l'interpretation : division par zero" << endl;
            exit(0); // plus tard on levera une exception
        }
    }
    else if (this->operateur=="et") {
        if (og==0) valeur = 0;
        else {
            if (od==1) valeur = 1;
        }
    }
    else if (this->operateur=="ou") {
        if (og==1) valeur = 1;
        else {
            if (od==0) valeur = 0;
        }
    }
    else if (this->operateur=="==") {
        if (og == od) valeur = 1;
    }
    else if (this->operateur=="!=") {
        if (og != od) valeur = 1;
    }
    else if (this->operateur=="<") {
        if (og < od) valeur = 1;
    }
    else if (this->operateur=="<=") {
        if (og <= od) valeur = 1;
    }
    else if (this->operateur==">") {
        if (og > od) valeur = 1;
    }
    else if (this->operateur==">=") {
        if (og >= od) valeur = 1;
    }
    else if (this->operateur=="!") {
        if (od == 0) valeur = 1;
    }
    return valeur;
}

////////////////////////////////////////////////////////////////////////////////
// void NoeudOperateurBinaire::afficher(unsigned short indentation) {
//   Noeud::afficher(indentation);
//   cout << "Noeud - Operateur Binaire \"" << this->operateur.getChaine() << "\" applique a : " << endl;
//   operandeGauche->afficher(indentation+1);  // on affiche fils gauche et fils droit
//   operandeDroit->afficher(indentation+1);   // en augmentant l'indentation
// }

void NoeudOperateurBinaire::Accept (NoeudVisitor& visitor) {
    if (typeid(visitor) == typeid(AfficherVisitor))
        visitor.IncresIndentation();
    visitor.VisitNoeudOperateurBinaire(this);
    operandeGauche->Accept(visitor);
    operandeDroit->Accept(visitor);
    if (typeid(visitor) == typeid(AfficherVisitor))
        visitor.DecresIndentation();
}
